package opentracing

import (
	"context"
	"time"

	//"time"

	"github.com/micro/go-micro/v2/client"
	"github.com/micro/go-micro/v2/registry"
	"github.com/micro/go-micro/v2/server"

	//"github.com/SkyAPM/go2sky/reporter"
)

type otWrapper struct {
	ot *OtTracer
	client.Client
}

//func StartSpanFromContext(ctx context.Context, tracer opentracing.Tracer, name string, opts ...opentracing.StartSpanOption) (context.Context, opentracing.Span, error) {
//	md, ok := metadata.FromContext(ctx)
//	if !ok {
//		md = make(metadata.Metadata)
//	}
//
//	// Find parent span.
//	// First try to get span within current service boundary.
//	// If there doesn't exist, try to get it from go-micro metadata(which is cross boundary)
//	if parentSpan := opentracing.SpanFromContext(ctx); parentSpan != nil {
//		opts = append(opts, opentracing.ChildOf(parentSpan.Context()))
//	} else if spanCtx, err := tracer.Extract(opentracing.TextMap, opentracing.TextMapCarrier(md)); err == nil {
//		opts = append(opts, opentracing.ChildOf(spanCtx))
//	}
//
//	sp := tracer.StartSpan(name, opts...)
//
//	if err := sp.Tracer().Inject(sp.Context(), opentracing.TextMap, opentracing.TextMapCarrier(md)); err != nil {
//		return nil, nil, err
//	}
//
//	ctx = opentracing.ContextWithSpan(ctx, sp)
//	ctx = metadata.NewContext(ctx, md)
//	return ctx, sp, nil
//}

func (o *otWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
	name := req.Endpoint()
	peer := req.Service()

	otSpan, err := o.ot.CreateExitSpan(ctx, name, peer)
	if err != nil {
		//如果构建otSpan失败，直接执行下一步
		return o.Client.Call(ctx, req, rsp, opts...)
	}
	defer otSpan.End()

	//如果构建otSpan成功

	////从req 获取 信息， 根据proto中的 trace_fileld
	otSpan.ExtractTraceField(req.Body(), "req")

	////执行下一步
	callErr := o.Client.Call(otSpan.newCtx, req, rsp, opts...)

	////执行完毕，从rsp 获取 信息
	if nil != callErr{
		otSpan.Error(time.Now(), callErr.Error())
	}else{
		//从rsp 获取 信息， 根据proto中的 trace_fileld
		if nil != rsp{
			otSpan.ExtractTraceField(rsp, "rsp")
		}
	}

	return callErr
}

func (o *otWrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
	name := req.Endpoint()
	peer := req.Service()
	otSpan, err := o.ot.CreateExitSpan(ctx, name, peer)
	if err != nil {
		//如果构建otSpan失败，直接执行下一步
		return o.Client.Stream(ctx, req, opts...)
	}
	defer otSpan.End()

	//如果构建otSpan成功

	////从req 获取 信息， 根据proto中的 trace_fileld
	otSpan.ExtractTraceField(req.Body(), "req")

	////执行下一步
	stream, callErr := o.Client.Stream(ctx, req, opts...)

	////执行完毕，此处只表示生成stream
	if nil != callErr{
		otSpan.Error(time.Now(), callErr.Error())
	}

	return stream, callErr
}

func (o *otWrapper) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
	name := "publish"
	peer := p.Topic()
	otSpan, err := o.ot.CreateExitSpan(ctx, name, peer)
	if err != nil {
		//如果构建otSpan失败，直接执行下一步
		return o.Client.Publish(ctx, p, opts...)
	}
	defer otSpan.End()

	//如果构建otSpan成功

	////从req 获取 信息， 根据proto中的 trace_fileld
	otSpan.ExtractTraceField(p.Payload(), "pub")

	////执行下一步
	pubErr := o.Client.Publish(ctx, p, opts...)

	////执行完毕，此处只表示生成stream
	if nil != pubErr{
		otSpan.Error(time.Now(), pubErr.Error())
	}
	return pubErr
}

// NewClientWrapper accepts an open tracing Trace and returns a Client Wrapper
func NewClientWrapper(tracer *OtTracer) client.Wrapper {
	var ot *OtTracer
	if nil != tracer {
		ot = tracer
	}else{
		ot = GlobalTracer()
	}
	return func(c client.Client) client.Client {
		return &otWrapper{ot, c}
	}
}

// NewCallWrapper accepts an opentracing Tracer and returns a Call Wrapper
func NewCallWrapper(tracer *OtTracer) client.CallWrapper {
	var ot *OtTracer
	if nil != tracer {
		ot = tracer
	}else{
		ot = GlobalTracer()
	}

	return func(cf client.CallFunc) client.CallFunc {
		return func(ctx context.Context, node *registry.Node, req client.Request, rsp interface{}, opts client.CallOptions) error {

			name := req.Endpoint()
			peer := req.Service()
			otSpan, err := ot.CreateExitSpan(ctx, name, peer)
			if err != nil {
				//如果构建otSpan失败，直接执行下一步
				return cf(ctx, node, req, rsp, opts)
			}
			defer otSpan.End()

			//如果构建otSpan成功

			////从req 获取 信息， 根据proto中的 trace_fileld
			otSpan.ExtractTraceField(req.Body(), "req")

			////执行下一步
			callErr := cf(ctx, node, req, rsp, opts)

			////执行完毕，从rsp 获取 信息
			if nil != callErr{
				otSpan.Error(time.Now(), callErr.Error())
			}else{
				//从rsp 获取 信息， 根据proto中的 trace_fileld
				if nil != rsp{
					otSpan.ExtractTraceField(rsp, "rsp")
				}
			}

			return callErr
		}
	}
}

// NewHandlerWrapper accepts an opentracing Tracer and returns a Handler Wrapper
func NewHandlerWrapper(tracer *OtTracer) server.HandlerWrapper {
	var ot *OtTracer
	if nil != tracer {
		ot = tracer
	}else{
		ot = GlobalTracer()
	}

	return func(h server.HandlerFunc) server.HandlerFunc {
		return func(ctx context.Context, req server.Request, rsp interface{}) error {
			//name := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
			name :=  req.Endpoint()
			otSpan, err := ot.CreateEntrySpan(ctx, name)
			if err != nil {
				return err
			}
			defer otSpan.End()

			//如果构建otSpan成功

			////msg 获取 信息， 根据proto中的 trace_fileld
			otSpan.ExtractTraceField(req.Body(), "req")

			////执行下一步
			callErr := h(ctx, req, rsp)

			////执行完毕
			if nil != callErr{
				otSpan.Error(time.Now(), callErr.Error())
			}else{
				//从rsp 获取 信息， 根据proto中的 trace_fileld
				if nil != rsp{
					otSpan.ExtractTraceField(rsp, "rsp")
				}
			}
			return callErr
		}
	}
}

// NewSubscriberWrapper accepts an opentracing Tracer and returns a Subscriber Wrapper
func NewSubscriberWrapper(tracer *OtTracer) server.SubscriberWrapper {
	var ot *OtTracer
	if nil != tracer {
		ot = tracer
	}else{
		ot = GlobalTracer()
	}

	return func(next server.SubscriberFunc) server.SubscriberFunc {
		return func(ctx context.Context, msg server.Message) error {
			name := msg.Topic()
			otSpan, err := ot.CreateEntrySpan(ctx, name)
			if err != nil {
				return err
			}
			defer otSpan.End()

			//如果构建otSpan成功

			////msg 获取 信息， 根据proto中的 trace_fileld
			otSpan.ExtractTraceField(msg.Payload(), "sub")

			////执行下一步
			subErr := next(otSpan.newCtx, msg)

			////执行完毕
			if nil != subErr{
				otSpan.Error(time.Now(), subErr.Error())
			}
			return subErr
		}
	}
}

